/*
 * 描述： <描述>
 * 修改人： rain
 * 修改时间： 2016年1月20日
 * 项目： rainhy-project-hoe
 */
package com.rainhy.webcore.handler;

import java.io.IOException;
import java.nio.charset.Charset;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.rainhy.core.exception.RainhyIllegalArgException;
import com.rainhy.webcore.support.bean.ReturnAjax;

/**
 * 
 * Rainhy 异常处理器
 * 
 * @author rain
 * @version [版本号, 2015年12月15日]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
public class RainhyHandlerExceptionResolver extends SimpleMappingExceptionResolver implements HandlerExceptionResolver {
    
    /** 日志记录 */
    private static Logger logger = LoggerFactory.getLogger(RainhyHandlerExceptionResolver.class);
    
    /** ajax请求异常跳转地址 */
    private String ajaxErrorToPage;
    
    /** ajax请求异常跳转地址 */
    public String getAjaxErrorToPage() {
        return ajaxErrorToPage;
    }
    
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        if (ex != null) {
            logger.error(ex.toString(), ex);
            
            if (isAjax(request)) {  // ajax 调用
                if (handler instanceof HandlerMethod) {
                    HandlerMethod handlerMethod = (HandlerMethod) handler;
                    ResponseBody responseBody = handlerMethod.getMethodAnnotation(ResponseBody.class);
                    Class<?> returnType = handlerMethod.getMethod().getReturnType();
                    
                    // Ajax 调用返回 ReturnAjax.class类型
                    if (responseBody != null && returnType != null
                            && (ClassUtils.isAssignable(returnType, ReturnAjax.class)
                                    || ClassUtils.isAssignable(returnType, Void.class)
                                    || StringUtils.equals(returnType.getName(), "void"))) {
                        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); // 设置返回类型为 json
                        ReturnAjax returnAjax = new ReturnAjax(ex);
                        if (StringUtils.isNotBlank(ajaxErrorToPage) && !(ex instanceof RainhyIllegalArgException)) { // 只要不是非法参数异常,全部设置跳转路径到 ajax请求异常跳转地址
                            returnAjax.setToUrl(request.getContextPath().concat(ajaxErrorToPage));
                        }
                        try {
                            String json = new ObjectMapper().writeValueAsString(returnAjax);
                            IOUtils.write(json, response.getOutputStream(), Charset.defaultCharset());
                            return new ModelAndView();
                        } catch (IOException e) {
                            logger.warn(e.getMessage(), e);
                        }
                    }
                }
            }
        }
        
        ModelAndView mav = super.resolveException(request, response, handler, ex);
        if (ex != null) {
            mav.addObject("exceptionMessage", ex.getMessage());
        }
        return mav;
    }
    
    /** ajax请求异常跳转地址 */
    public void setAjaxErrorToPage(String ajaxErrorToPage) {
        this.ajaxErrorToPage = ajaxErrorToPage;
    }
    
    /**
     * 
     * 判断是否 Ajax 请求
     *
     * @param request
     * 
     * @return boolean [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     * @version [版本号, 2015年12月17日]
     * @author rain
     */
    private boolean isAjax(HttpServletRequest request) {
        return "XMLHttpRequest".equals(request.getHeader("X-Requested-With"));
    }
}
